package crons

import (
	context2 "context"
	"encoding/json"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/app/ops/services"
	"go.didapinche.com/foundation/plat-echo/app/ops/zclients"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/plat_limos_rpc"
	dtime "go.didapinche.com/time"
	"go.uber.org/zap"
	"time"
)

type AppSyncJob struct {
	logger          *zap.Logger
	appRepo         repositories.ApplicationRepository
	apiRepo         repositories.ApiRepository
	template        services.TemplateService
	k8s             services.K8sService
	limosService    plat_limos_rpc.TChanLimosService
	graphRepository repositories.GraphRepository
}

func NewAppSyncJob(limosService plat_limos_rpc.TChanLimosService, logger *zap.Logger, graphRepository repositories.GraphRepository,
	appRepo repositories.ApplicationRepository, apiRepo repositories.ApiRepository, template services.TemplateService, k8s services.K8sService) *AppSyncJob {
	job := &AppSyncJob{
		limosService:    limosService,
		template:        template,
		apiRepo:         apiRepo,
		k8s:             k8s,
		graphRepository: graphRepository,
		logger:          logger.With(zap.String("type", "crons.AppSyncJob")),
		appRepo:         appRepo,
	}
	return job
}

//将app的信息定时更新到数据库中
func (a *AppSyncJob) StoreAppInfo() error {
	context := context2.Background()
	nodes := a.template.GetNodeByGroup(context)
	all, _ := a.appRepo.FindAll(context)
	apps, err := zclients.FindApp(context, a.limosService)
	if err != nil {
		return errors.Wrap(err, "query limos error")
	}
	appMap := map[uint64]*models.Application{}
	for _, application := range all {
		appMap[application.ID] = application
	}
	for _, app := range apps.Apps {

		var owners []*models.ApplicationOwner
		for i, id := range app.OwnerIds {
			owners = append(owners, &models.ApplicationOwner{
				UserID:   id,
				UserName: app.Owner[i],
			})
		}
		application := a.convert(app, owners)
		application2, err := a.appRepo.FindByID(context, application.ID)
		if err == nil {
			user := make([]*models.User, 0)
			for i, id := range app.OwnerIds {
				user = append(user, &models.User{
					ID:   id,
					Name: app.Owner[i],
				})
			}
			owner, _ := json.Marshal(user)
			if application2.Owner != application.Owner || application2.Level != application.Level {
				apis, err := a.apiRepo.FindByAppName(context, application.Name)
				if err == nil {
					ids := make([]uint64, 0)
					for _, api := range apis {
						ids = append(ids, api.ID)
					}
					a.graphRepository.UpdateGraphByApiID(context, ids, string(owner), application.Level)

				}
				a.apiRepo.UpdateByAppName(context, application.Name, application.Owner, application.Level)
				a.graphRepository.UpdateGraphByAppID(context, application.ID, string(owner), application.Level)

			}
		}
		//更新应用信息
		err = a.appRepo.Save(context, application)

		if err != nil {
			a.logger.Warn("sync app info error:%s", zap.Error(err))
		}

		if appMap[application.ID] != nil {
			delete(appMap, application.ID)
		} else {
			// 新应用添加监控文件
			templates, err := a.template.FindByAppID(context, uint64(app.ID))
			if err != nil {
				a.logger.Error("call templateService FindByAppID error", zap.Error(err))
			}
			graphs := a.template.ChangeTemplate(context, templates, uint64(app.ID), nodes)
			if err := a.graphRepository.Creates(context, graphs); err != nil {
				a.logger.Error("call graphRepository Creates error", zap.Error(err))
			}
			a.template.UpdateK8s(context, int(app.ID), 0, app.Name)
		}
	}

	// 删除应用
	for _, app := range appMap {
		err := a.appRepo.DeleteById(context, app.ID)
		if err != nil {
			a.logger.Warn("delete app err", zap.Error(err))
		}
		//删除掉对应监控配置文件
		err = a.k8s.DeleteAlertResource(context, app.Name)
		if err != nil {
			a.logger.Warn("delete graph err", zap.Error(err))
		}
	}
	return nil
}

//将limos的app转化为models.Application
func (a *AppSyncJob) convert(app *plat_limos_rpc.App, owners []*models.ApplicationOwner) *models.Application {
	application := new(models.Application)
	application.ID = uint64(app.ID)
	application.Name = app.Name
	application.DevGroupID = app.DevGroupID
	application.Description = app.Description
	createTime, err := time.Parse(data.FormatLayout, app.CreateTime)
	if err == nil {
		application.CreateTime = dtime.Time{createTime}
	}
	updateTime, err := time.Parse(data.FormatLayout, app.UpdateTime)
	if err == nil {
		application.UpdateTime = dtime.Time{updateTime}
	}
	if len(owners) > 0 {
		bytes, e := json.Marshal(owners)
		if e != nil {
			a.logger.Error("app user info marshal error", zap.Error(e))
		}
		application.Owner = string(bytes)
	}
	application.Level = int(app.Level)
	application.Language = app.Language
	application.GitURL = app.GitURL
	application.DevGroupName = app.DevGroupName
	return application
}

func (a *AppSyncJob) Init() {
	err := a.StoreAppInfo()
	if err != nil {
		a.logger.Error("app_sync_job run error", zap.Error(err))
	}
}

func (a *AppSyncJob) Run() {
	err := a.StoreAppInfo()
	if err != nil {
		a.logger.Error("app_sync_job run error", zap.Error(err))
	}
}

func (a *AppSyncJob) Cron() string {
	return "0 0 0/1 * * ?"
}

func (a *AppSyncJob) Name() string {
	return "app_sync"
}
